import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import com.rarebot.event.events.MessageEvent;
import com.rarebot.event.listeners.MessageListener;
import com.rarebot.event.listeners.PaintListener;
import com.rarebot.script.Script;
import com.rarebot.script.ScriptManifest;
import com.rarebot.script.methods.Skills;
import com.rarebot.script.util.Timer;
import com.rarebot.script.wrappers.RSComponent;

@ScriptManifest(name = "PsiCraft", authors = { "Psi" }, keywords = { "Crafting, gem, leather" }, description = "Currently supports cutting all gems", version = .10)
public class PsiCraft extends Script implements PaintListener, MessageListener,
		MouseListener {

	private int MATERIAL_ID, PRODUCT_ID;

	public PsiCraftGui GUI;
	private static final List<Scenario> scenarios = new LinkedList<Scenario>();
	private boolean clay;
	private static final int SC_NEEDLE = 14113, OPAL = 1609, JADE = 1611,
			TOPAZ = 1613, SAPPHIRE = 1607, EMERALD = 1605, RUBY = 1603,
			DIAMOND = 1601, DRAGONSTONE = 1615, ONYX = 6573;
	private String status;

	private long startTime, minutes, hours, timeTL;
	private int nextLevel, PercTL, startXP, xpTL;
	public boolean showhide = true, showhidehover = false;
	public int cutCount = 0;
	private final Image hide = getImage("http://img713.imageshack.us/img713/3607/hideg.png");
	private final Image hideHover = getImage("http://img850.imageshack.us/img850/6796/hidehover.png");
	private final Image hideClicked = getImage("http://img577.imageshack.us/img577/2936/hideclicked.png");
	private final Color color1 = new Color(204, 204, 255, 180);
	private final Color color2 = new Color(0, 0, 0);
	private final Color color3 = new Color(255, 0, 0, 130);
	private final Color color4 = new Color(0, 255, 51, 130);

	private final BasicStroke stroke1 = new BasicStroke(1);

	private final Font font1 = new Font("Consolas", 3, 17);
	private final Font font2 = new Font("Consolas", 1, 13);
	private final Font font3 = new Font("MV Boli", 0, 11);

	private Image getImage(String url) {
		try {
			return ImageIO.read(new URL(url));
		} catch (IOException e) {
			return null;
		}
	}

	private static final Color MOUSE_COLOR = new Color(132, 198, 99),
			MOUSE_BORDER_COLOR = new Color(225, 200, 25),
			MOUSE_CENTER_COLOR = new Color(168, 9, 9);
	public static final int GEM_INTERFACE = 905, GEM_CHILD = 14;
	public static final int[] CUT_ANIMS = { 884, 885, 886, 887, 888, 889, 890,
			891, 892 };
	Point p, p2;
	private final LinkedList<MousePathPoint> mousePath = new LinkedList<MousePathPoint>();
	private boolean customMouse = true;
	private final RenderingHints antialiasing = new RenderingHints(
			RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

	private int smashCount;

	private int totalCount;

	public static String format(double d) {
		boolean neg = d < 0;
		d = Math.abs(d);
		if (d >= 1000000) {
			String k = String.valueOf(d / 1000000D);
			String val = k.substring(0, k.indexOf(".") + 2);
			if (val.endsWith(".0"))
				val = val.substring(0, val.indexOf('.'));
			return (neg ? "-" : "") + val + "m";
		} else if (d >= 1000) {
			String k = String.valueOf(d / 1000D);
			String val = k.substring(0, k.indexOf(".") + 2);
			if (val.endsWith(".0"))
				val = val.substring(0, val.indexOf('.'));
			return (neg ? "-" : "") + val + "k";
		} else
			return (neg ? "-" : "") + String.valueOf((int) d);
	}

	public boolean onStart() {
		try {
			SwingUtilities.invokeAndWait(new Runnable() {
				public void run() {
					GUI = new PsiCraftGui();
					GUI.setVisible(true);
				}
			});

		} catch (InterruptedException e) {
		} catch (InvocationTargetException e) {
		}

		while (GUI.isVisible()) {
			status = "Settings";
			sleep(50);
		}
		scenarios.add(new Bank());
		scenarios.add(new Cut());
		log("Material: " + MATERIAL_ID);
		log("PRODUCT: " + PRODUCT_ID);
		return true;
	}

	public void onFinish() {
		log("Thank you for using PsiCraft. You gained "
				+ (skills.getCurrentExp(Skills.CRAFTING) - startXP)
				+ " xp in less than " + (hours + 1) + " hours!");
		stop();
	}

	@Override
	public void onRepaint(Graphics g1) {
		if (startTime == 0 || !game.isLoggedIn())
			showhide = false;
		if (customMouse) {
			drawMouse(g1);
			Point p = mouse.getLocation();
			while (!mousePath.isEmpty() && mousePath.peek().isUp())
				mousePath.remove();
			Point clientCursor = mouse.getLocation();
			MousePathPoint mpp = new MousePathPoint(clientCursor.x,
					clientCursor.y, 3000);
			if (mousePath.isEmpty() || !mousePath.getLast().equals(mpp))
				mousePath.add(mpp);
			MousePathPoint lastPoint = null;
			for (MousePathPoint a : mousePath) {
				if (lastPoint != null) {
					g1.setColor(a.getColor());
					g1.drawLine(a.x, a.y, lastPoint.x, lastPoint.y);
				}
				lastPoint = a;
			}
			g1.fillRect(p.x - 5, p.y, 12, 2);
			g1.fillRect(p.x, p.y - 5, 2, 12);
		}
		if (showhide) {
			nextLevel = skills.getCurrentLevel(Skills.CRAFTING) + 1;
			PercTL = skills.getPercentToNextLevel(Skills.CRAFTING);
			xpTL = skills.getExpToNextLevel(Skills.CRAFTING);
			long runTime = System.currentTimeMillis() - startTime;
			long seconds = runTime / 1000;
			if (seconds >= 60) {
				minutes = seconds / 60;
				seconds -= (minutes * 60);
			}
			if (minutes >= 60) {
				hours = minutes / 60;
				minutes -= (hours * 60);
			}
			final int gainedXp = skills.getCurrentExp(Skills.CRAFTING)
					- startXP;
			Graphics2D g = (Graphics2D) g1;
			g.setRenderingHints(antialiasing);
			g.setColor(color1);
			g.fillRoundRect(548, 204, 188, 146, 16, 16);
			g.setColor(color2);
			g.setStroke(stroke1);
			g.drawRoundRect(548, 204, 188, 146, 16, 16);
			g.setFont(font1);
			ScriptManifest manifest = PsiCraft.class
					.getAnnotation(ScriptManifest.class);
			g.drawString(manifest.name() + " v" + manifest.version(), 550, 225);
			g.setFont(font2);
			g.drawString("Time Running: " + hours + ":" + minutes + ":"
					+ seconds, 550, 250);
			if (gainedXp == 0) {
				g.drawString("Status: Starting script...", 550, 265);
				return;
			}
			g.drawString("Status: " + status, 550, 265);
			int xpPerHour = (int) ((3600000.0 / (double) runTime) * gainedXp);
			int craftsPerHour = (int) ((3600000.0 / (double) runTime) * cutCount);
			g.drawString("Exp/hr: " + xpPerHour, 550, 280);
			g.drawString("Exp Gained: " + gainedXp, 550, 295);
			g.drawString("Gems Cut: " + cutCount, 550, 310);
			g.drawString("Gems/hr: " + (craftsPerHour), 550, 325);
			timeTL = (long) xpTL * runTime / ((long) gainedXp);
			g.drawString("TTL: " + Timer.format(timeTL) + " (" + format(xpTL)
					+ " xp)", 550, 340);
			g.setColor(color3);
			g.fillRoundRect(3, 307, 513, 31, 16, 16);
			g.setColor(color4);
			g.fillRoundRect(3, 307, 513 * PercTL / 100, 31, 16, 16);
			g.setFont(font3);
			g.setColor(color2);
			g.drawString(PercTL + "% to Level " + nextLevel, 207, 328);
		}
		Image hideButton = hideClicked;
		if (showhidehover) {
			hideButton = hideHover;
		} else if (showhide) {
			hideButton = hide;
		}
		g1.drawImage(hideButton, 717, 204, null);

	}

	private void drawMouse(Graphics g1) {
		((Graphics2D) g1).setRenderingHints(new RenderingHints(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON));
		Point p = mouse.getLocation();
		Graphics2D spinG = (Graphics2D) g1.create();
		Graphics2D spinGRev = (Graphics2D) g1.create();
		Graphics2D spinG2 = (Graphics2D) g1.create();
		spinG.setColor(MOUSE_BORDER_COLOR);
		spinGRev.setColor(MOUSE_COLOR);
		spinG.rotate(System.currentTimeMillis() % 2000d / 2000d * (360d) * 2
				* Math.PI / 180.0, p.x, p.y);
		spinGRev.rotate(System.currentTimeMillis() % 2000d / 2000d * (-360d)
				* 2 * Math.PI / 180.0, p.x, p.y);
		final int outerSize = 20;
		final int innerSize = 12;
		spinG.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		spinGRev.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		spinG.drawArc(p.x - (outerSize / 2), p.y - (outerSize / 2), outerSize,
				outerSize, 100, 75);
		spinG.drawArc(p.x - (outerSize / 2), p.y - (outerSize / 2), outerSize,
				outerSize, -100, 75);
		spinGRev.drawArc(p.x - (innerSize / 2), p.y - (innerSize / 2),
				innerSize, innerSize, 100, 75);
		spinGRev.drawArc(p.x - (innerSize / 2), p.y - (innerSize / 2),
				innerSize, innerSize, -100, 75);
		g1.setColor(MOUSE_CENTER_COLOR);
		g1.fillOval(p.x, p.y, 2, 2);
		spinG2.setColor(MOUSE_CENTER_COLOR);
		spinG2.rotate(System.currentTimeMillis() % 2000d / 2000d * 360d
				* Math.PI / 180.0, p.x, p.y);
		spinG2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND,
				BasicStroke.JOIN_ROUND));
		spinG2.drawLine(p.x - 5, p.y, p.x + 5, p.y);
		spinG2.drawLine(p.x, p.y - 5, p.x, p.y + 5);
	}

	@SuppressWarnings("serial")
	private class MousePathPoint extends Point {
		private int toColor(double d) {
			return Math.min(255, Math.max(0, (int) d));
		}

		private long finishTime;
		private double lastingTime;

		public MousePathPoint(int x, int y, int lastingTime) {
			super(x, y);
			this.lastingTime = lastingTime;
			finishTime = System.currentTimeMillis() + lastingTime;
		}

		public boolean isUp() {
			return System.currentTimeMillis() > finishTime;
		}

		public Color getColor() {
			return new Color(
					0,
					153,
					0,
					toColor(256 * ((finishTime - System.currentTimeMillis()) / lastingTime)));
		}
	}

	@Override
	public void messageReceived(MessageEvent m) {
		String message = m.getMessage();
		if (message.contains("You cut the ")) {
			cutCount++;
			totalCount++;
		}
		if (message.contains("You mis-hit the chisel and smash the ")) {
			smashCount++;
			totalCount++;
		}
		if (message.contains("You could not bank all of your items.")
				|| message
						.contains("wish to store any more items in the bank.")) {
			log("You don't have enough space in your bank account.");
			stopScript();
		}
	}

	@Override
	public int loop() {
		while (skills.getRealLevel(Skills.CONSTITUTION) < 1)
			sleep(50);
		if (startTime == 0 && skills.getRealLevel(Skills.CONSTITUTION) > 1) {
			startTime = System.currentTimeMillis();
			startXP = skills.getCurrentExp(Skills.CRAFTING);
		}
		try {
			camera.setPitch(100);
		} catch (NullPointerException exception) {
		}
		try {
			for (Scenario scenario : scenarios) {
				status = scenario.getStatus();
				scenario.execute();
			}
		} catch (NullPointerException error) {
			error.printStackTrace();
		}
		return 0;
	}

	public interface Scenario {
		public boolean isValid();

		public void execute();

		public String getStatus();
	}

	public class Bank implements Scenario {

		public boolean isValid() {
			return !inventory.contains(MATERIAL_ID);
		}

		public void execute() {
			if (!isValid())
				return;
			int i = 0;
			while (!bank.open() && i < 10) {
				if (players.getMyPlayer().isMoving()) {
					i = 0;
					sleep(2000, 3200);
				}
				sleep(300, 500);
			}
			if (i == 10) {
				log("Couldn't find a bank!");
				stopScript();
			}
			if (clay)
				bank.depositAllExcept(SC_NEEDLE);
			else
				bank.depositAll();

			for (i = 0; i < 10; i++) {
				if (bank.withdraw(MATERIAL_ID, 0)
						|| inventory.contains(MATERIAL_ID))
					break;
				if (i == 9) {
					log("You are out of materials!");
					stopScript();
				}
				sleep(300, 500);
			}
			bank.close();
		}

		public String getStatus() {
			return isValid() ? "Banking" : status;
		}

	}

	public class Cut implements Scenario {

		public boolean isValid() {
			int anim = players.getMyPlayer().getAnimation();
			boolean busy = false;
			for (int i : CUT_ANIMS)
				if (anim == i)
					busy = true;
			return (inventory.contains(MATERIAL_ID) && !busy);
		}

		public void execute() {
			if (!isValid())
				return;
			if (inventory.contains(PRODUCT_ID)) {
				for (int i = 0; i < 10; i++) {
					int anim = players.getMyPlayer().getAnimation();
					for (int j : CUT_ANIMS)
						if (anim == j)
							return;
					sleep(100, 300);
				}
			}
			RSComponent clickCut = interfaces.getComponent(GEM_INTERFACE,
					GEM_CHILD);
			int i = 0;
			while (i < 10 && (!clickCut.isValid() || clickCut == null)) {
				inventory.getItem(MATERIAL_ID).doClick(true);
				sleep(300, 700);
				clickCut = interfaces.getComponent(GEM_INTERFACE, GEM_CHILD);
				i++;
			}
			if (i == 10) {
				log("Unable to cut gems.");
				stopScript();
			}
			clickCut.interact("Cut All");
		}

		public String getStatus() {
			return isValid() ? "Cutting gems" : status;
		}

	}

	@Override
	public void mouseClicked(MouseEvent e) {
		int x = e.getX();
		int y = e.getY();
		if (x >= 717 && x < 737 && y >= 204 && y < 224) {
			if (showhide) {
				showhide = false;
			} else {
				showhide = true;
			}
		}
	}

	@Override
	public void mouseEntered(MouseEvent e) {

	}

	@Override
	public void mouseExited(MouseEvent e) {

	}

	@Override
	public void mousePressed(MouseEvent e) {
	}

	@Override
	public void mouseReleased(MouseEvent e) {
	}

	public enum materialToUse {

		Opal("Opal", 1625, OPAL), Jade("Jade", 1627, JADE), Topaz("Red Topaz",
				1629, TOPAZ), Sapphire("Sapphire", 1623, SAPPHIRE), Emerald(
				"Emerald", 1621, EMERALD), Ruby("Ruby", 1619, RUBY), Diamond(
				"Diamond", 1617, DIAMOND), Dragonstone("Dragonstone", 1631,
				DRAGONSTONE), Onyx("Onyx", 6571, ONYX);

		public String name;
		public int itemId;
		private int prodId;

		materialToUse(String name, int itemId, int prodId) {
			this.name = name;
			this.itemId = itemId;
			this.prodId = prodId;
		}

		public String getName() {
			return name;
		}

		public int getId() {
			return itemId;
		}

		public int getProdId() {
			return prodId;
		}
	}

	@SuppressWarnings("serial")
	public class PsiCraftGui extends JFrame {
		public PsiCraftGui() {
			initComponents();
		}

		private void button1ActionPerformed(ActionEvent e) {
			clay = checkBox1.isSelected();
			for (materialToUse material : materialToUse.values()) {
				if (comboBox1.getSelectedItem().toString()
						.equals(material.getName())) {
					MATERIAL_ID = material.getId();
					PRODUCT_ID = material.getProdId();
					dispose();
				}
			}
		}

		private void button2ActionPerformed(ActionEvent e) {
			dispose();
			stopScript();
		}

		@SuppressWarnings({ "unchecked", "rawtypes" })
		private void initComponents() {
			// JFormDesigner - Component initialization - DO NOT MODIFY
			// //GEN-BEGIN:initComponents
			// Generated using JFormDesigner Evaluation license - Psi
			label1 = new JLabel();
			comboBox1 = new JComboBox();
			label2 = new JLabel();
			button1 = new JButton();
			checkBox1 = new JCheckBox();
			button2 = new JButton();

			// ======== this ========
			setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
			setResizable(false);
			setTitle("PsiCraft Settings");
			setAlwaysOnTop(true);
			Container contentPane = getContentPane();
			contentPane.setLayout(null);

			// ---- label1 ----
			ScriptManifest manifest = PsiCraft.class
					.getAnnotation(ScriptManifest.class);
			label1.setText(manifest.name() + " v" + manifest.version());
			label1.setFont(new Font("Segoe Print", Font.PLAIN, 26));
			contentPane.add(label1);
			label1.setBounds(new Rectangle(new Point(65, 20), label1
					.getPreferredSize()));

			// ---- comboBox1 ----
			comboBox1.setMaximumRowCount(9);
			comboBox1.setModel(new DefaultComboBoxModel(new String[] { "Opal",
					"Jade", "Red Topaz", "Sapphire", "Emerald", "Ruby",
					"Diamond", "Dragonstone", "Onyx" }));
			contentPane.add(comboBox1);
			comboBox1.setBounds(95, 90, comboBox1.getPreferredSize().width, 25);

			// ---- label2 ----
			label2.setText("Gem to Cut: ");
			label2.setLabelFor(comboBox1);
			contentPane.add(label2);
			label2.setBounds(30, 90, label2.getPreferredSize().width, 25);

			// ---- button1 ----
			button1.setText("Start");
			button1.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					button1ActionPerformed(e);
				}
			});
			contentPane.add(button1);
			button1.setBounds(new Rectangle(new Point(90, 140), button1
					.getPreferredSize()));

			// ---- checkBox1 ----
			checkBox1.setText("Clay Needle?");
			contentPane.add(checkBox1);
			checkBox1.setBounds(new Rectangle(new Point(205, 90), checkBox1
					.getPreferredSize()));

			// ---- button2 ----
			button2.setText("Exit");
			button2.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					button2ActionPerformed(e);
				}
			});
			contentPane.add(button2);
			button2.setBounds(new Rectangle(new Point(195, 140), button2
					.getPreferredSize()));

			{ // compute preferred size
				Dimension preferredSize = new Dimension();
				for (int i = 0; i < contentPane.getComponentCount(); i++) {
					Rectangle bounds = contentPane.getComponent(i).getBounds();
					preferredSize.width = Math.max(bounds.x + bounds.width,
							preferredSize.width);
					preferredSize.height = Math.max(bounds.y + bounds.height,
							preferredSize.height);
				}
				Insets insets = contentPane.getInsets();
				preferredSize.width += insets.right;
				preferredSize.height += insets.bottom;
				contentPane.setMinimumSize(preferredSize);
				contentPane.setPreferredSize(preferredSize);
			}
			setSize(345, 220);
			setLocationRelativeTo(getOwner());
			// //GEN-END:initComponents
		}

		// JFormDesigner - Variables declaration - DO NOT MODIFY
		// //GEN-BEGIN:variables
		// Generated using JFormDesigner Evaluation license - Psi
		private JLabel label1;
		@SuppressWarnings("rawtypes")
		private JComboBox comboBox1;
		private JLabel label2;
		private JButton button1;
		private JCheckBox checkBox1;
		private JButton button2;
		// JFormDesigner - End of variables declaration //GEN-END:variables
	}

}
